package application;

import java.awt.Toolkit;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import utils.config.Config;

import data.*;

import parser.ErlangParser;
import preprocessing.AvgAggregator;
import preprocessing.Normalizer;
import visual.GHeat;
import visual.KMLGraph;
import visual.KMLPrinterTime;

public class ErlangMap {
	
	public static void main(String[] args) throws Exception {
		ErlangMap em = new ErlangMap();
		
		em.run();	
	}
	
	
	private Calendar START;
	private Calendar END;
	private CityArea AREA;
	private String BASE_OUT_DIR;
	private boolean SPACE_NORM;
	private boolean TIME_NORM;
	private boolean PF_IPF_NORM;
	private int [] DAY_OF_WEEK;
	private final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
	
	public ErlangMap() {
		Config config = new Config(this.getClass().getSimpleName()+".ser"); 
		
		START = (Calendar)config.get("START");
		END = (Calendar)config.get("END");
		
		DAY_OF_WEEK = (int[])config.get("DAY_OF_WEEK");
		
		AREA = new CityArea((String)config.get("AREA_NAME"), 
				   (double[][])config.get("AREA_BBOX"),
				   (String[])config.get("BASE_IN_DIRS"));
		
		System.out.println(AREA.getRadius());
		
		BASE_OUT_DIR = (String)config.get("BASE_OUT_DIR");
		SPACE_NORM = (Boolean)config.get("SPACE_NORM");
		TIME_NORM = (Boolean)config.get("TIME_NORM");
		PF_IPF_NORM = (Boolean)config.get("PF_IPF_NORM");
	}
	
	
	
	public void run() throws Exception {
		
		final int[] DAYS = DAY_OF_WEEK;
		
		String S_DAYS = "";
		for(int i=0; i<DAYS.length;i++) {
				if(DAYS[i] == Calendar.MONDAY) S_DAYS = S_DAYS + "mon-";
				if(DAYS[i] == Calendar.TUESDAY) S_DAYS = S_DAYS + "tue-";
				if(DAYS[i] == Calendar.WEDNESDAY) S_DAYS = S_DAYS + "wed-";
				if(DAYS[i] == Calendar.THURSDAY) S_DAYS = S_DAYS + "thu-";
				if(DAYS[i] == Calendar.FRIDAY) S_DAYS = S_DAYS + "fri-";
				if(DAYS[i] == Calendar.SATURDAY) S_DAYS = S_DAYS + "sat-";
				if(DAYS[i] == Calendar.SUNDAY) S_DAYS = S_DAYS + "sun-";
		}
		
		
		
		final String OUT_DIR = FORMATTER.format(START.getTime())+"-"+FORMATTER.format(END.getTime())+
											  "-"+S_DAYS+
											  "-"+(SPACE_NORM ? "sn":"")+"-"+(TIME_NORM ? "tn":"")+"-"+(PF_IPF_NORM ? "pfipfn":"")+
											  "-"+AREA.getName()+"/";
		
		System.out.println(OUT_DIR);
		
		
		System.out.println("Start...");
		
		List<MatrixTime> ets = new ArrayList<MatrixTime>();
		
		for(String dir: AREA.getBase_in_dirs())
			parseDir(dir,ets, START, END, AREA, DAYS);
		
		
		System.out.println("Reading Complete...");
		
		
		
		List<MatrixTime> day_avg = AvgAggregator.day_avg(ets);
		System.out.println(day_avg.size());
		
	
		if(SPACE_NORM)
			Normalizer.space_normalize(day_avg);
		if(TIME_NORM) {
			
			double[][] norm = computeTemporalAvg(START, END, AREA);
			Normalizer.time_normalize(day_avg,norm);
		}
		
		Normalizer.normalize01(day_avg);
		
		//PF
		List<MatrixTime> erlang_avg = AvgAggregator.day_avg(ets);
		Normalizer.normalize01(erlang_avg);
		
		if(PF_IPF_NORM)
			Normalizer.pfipf_normalize(day_avg,erlang_avg);
		
		//last normalization
		Normalizer.normalize01(day_avg);
		
		
		new File(BASE_OUT_DIR+OUT_DIR).mkdir();
		ObjectOutputStream oout = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(new File(BASE_OUT_DIR+OUT_DIR+"day_avg.ser"))));
		oout.writeObject(day_avg);
		oout.close();
		
		
		
		KMLPrinterTime.printMap(day_avg, BASE_OUT_DIR+OUT_DIR+"map.kmz");
		KMLGraph.printMap(day_avg, BASE_OUT_DIR+OUT_DIR+"graph.kmz");
		//GHeat.printMap(day_avg, BASE_OUT_DIR+OUT_DIR);
		
		
		System.out.println("Done");
		
		for(int i=0; i<3;i++) {
			Toolkit.getDefaultToolkit().beep();
			Thread.sleep(1000);
		}
	}
	
	
	public static void parseDir(String dir, List<MatrixTime> ets, Calendar START, Calendar END, CityArea AREA, int[] DAYS) throws Exception {
		File fdir = new File(dir);
		File[] files = fdir.listFiles();
		for(File f: files) {
			String[] elements = f.getName().split("_");
			Calendar start = ErlangParser.parseDateFileName(elements[3]);
			Calendar end = ErlangParser.parseDateFileName(elements[4]);
			
			
			boolean include = false;
			for(int i=0; i<DAYS.length;i++) {
				if(start.get(Calendar.DAY_OF_WEEK) == DAYS[i]) 
					include = true;
			}
			
			if(include) {
				if(!start.before(START) && !end.after(END)) {
					ets.add(ErlangParser.parse(dir+f.getName(), AREA));
					
					if((start.get(Calendar.HOUR_OF_DAY) == 12) && start.get(Calendar.MINUTE) == 00)
						System.out.println(start.getTime());
					
				}
			}
		}
	}
	
	
	public static double[][] computeTemporalAvg(Calendar START, Calendar END, CityArea AREA) throws Exception  {
		double[][] norm = new double[AREA.getNrows()][AREA.getNcols()];
		double size[][]  = new double[norm.length][norm[0].length];
		
		for(String dir: AREA.getBase_in_dirs()) {
			File fdir = new File(dir);
			File[] files = fdir.listFiles();
			for(File f: files) {
				String[] elements = f.getName().split("_");
				Calendar start = ErlangParser.parseDateFileName(elements[3]);
				Calendar end = ErlangParser.parseDateFileName(elements[4]);
			    
				if(!start.before(START) && !end.after(END)) {
					double[][] m = ErlangParser.parse(dir+f.getName(), AREA).getMat();
					
					for(int i=0; i<m.length; i++)
					for(int j=0; j<m[i].length;j++) 
					if(m[i][j]>0) {
						norm[i][j] = norm[i][j] + m[i][j];
						size[i][j] ++;
					}
						
					if((start.get(Calendar.HOUR_OF_DAY) == 12) && start.get(Calendar.MINUTE) == 00)
						System.out.println(start.getTime());
						
				}
			}
		}
		
		for(int i=0; i<norm.length; i++)
		for(int j=0; j<norm[i].length;j++) 
			norm[i][j] = norm[i][j] / size[i][j]; 
		
		return norm;
	}
	
}
